﻿using BCCommon;
using BCData.TMS.FreightConfiguration;
using BCData.TMS.VehicleCategoryReference;
using BCDto.TMS.FreightConfiguration;
using BCDto.TMS.VehicleType;
using BCEntity.TMS.FreightConfiguration;
using BCService.Utils;
using BCService.TMS.VehicleTypeService;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using BCData.Common.Area;
using System.Linq;
using BCCommon.Extensions;
using BCCommon.TMS.Order;
using BCCommon.TMS.FreightEnums;
using BCEntity.Common.EntityNameConst;
using BCDto.Sim.Admin.Admin;

namespace BCService.TMS.FreightConfigurationService
{
    public class FreightConfigurationService : IFreightConfigurationService
    {
        #region 私有变量

        private readonly IVehicleTypeService vehicleTypeService;
        private readonly IFreightConfigurationData freightConfigurationData;
        private readonly IVehicleCategoryReferenceData vehicleCategoryReferenceData;
        private readonly ICountyData countyData;
        private readonly IRedisService redisService;
        private readonly string key = string.Format("{0}", EntityNameConst.FreightConfigurationEntity);
        private readonly string vehicleTypeKey = string.Format("{0}", EntityNameConst.VehicleTypeEntity);

        #endregion 私有变量

        #region 构造函数

        public FreightConfigurationService(IVehicleTypeService vehicleTypeService, IFreightConfigurationData freightConfigurationData, IVehicleCategoryReferenceData vehicleCategoryReferenceData,
            ICountyData countyData,
            IRedisService redisService)
        {
            this.vehicleTypeService = vehicleTypeService;
            this.freightConfigurationData = freightConfigurationData;
            this.vehicleCategoryReferenceData = vehicleCategoryReferenceData;
            this.countyData = countyData;
            this.redisService = redisService;
        }

        #endregion 构造函数

        #region 公共方法

        public int InsertAsync(string editer, FreightConfigurationRequestDto dto)
        {
            //1 检查车型是否存在
            CheckVehicleType(dto.VehicleTypeId);
            //2 检查运费配置项是否存在
            CheckFreightConfig(dto);
            //3 新增运费配置
            var entities = new List<FreightConfigurationEntity>();
            foreach (var referenceId in dto.VehicleCategoryReferenceIds)
            {
                var freightConfigEntity = dto.As<FreightConfigurationEntity>();
                freightConfigEntity.Editer = editer;
                freightConfigEntity.VehicleCategoryReferenceId = referenceId;
                freightConfigEntity.CreateTime = DateTime.Now;
                freightConfigEntity.UpdateTime = DateTime.Now;
                entities.Add(freightConfigEntity);
            }
            var freightConfigId = freightConfigurationData.InsertAllAsync(entities).GetAwaiter().GetResult();
            if (freightConfigId <= 0)
                throw new ArgumentException("运费配置添加失败");
            this.redisService.DeleteMultipleKey(key, false);
            return freightConfigId;
        }

        public int UpdateAsync(bool isSystemAccount, string editer, int freightConfigurationId,
            FreightConfigurationRequestDto dto)
        {
            //1 检查车型是否存在
            var vehicleType = CheckVehicleType(dto.VehicleTypeId);
            //2 检查运费配置是否存在
            CheckFreightConfig(freightConfigurationId);
            //3 修改运费配置
            FreightConfigurationEntity updateEntity;
            if (!isSystemAccount)
            {
                //3.1 不是超级管理员只能修改价格
                updateEntity = vehicleType.As<FreightConfigurationEntity>();
                updateEntity.MileagePrice = dto.MileagePrice;
                updateEntity.StartPrice = dto.StartPrice;
                updateEntity.Editer = editer;
                updateEntity.UpdateTime = DateTime.Now;
            }
            else
            {
                //3.2 超管随便整
                updateEntity = dto.As<FreightConfigurationEntity>();
                updateEntity.FreightConfigurationId = freightConfigurationId;
                updateEntity.Editer = editer;
                updateEntity.UpdateTime = DateTime.Now;
            }
            var updateStatus = freightConfigurationData.UpdateAsync(updateEntity).GetAwaiter().GetResult();
            if (updateStatus == 0)
                throw new ArgumentException("运费配置修改失败");
            this.redisService.DeleteMultipleKey(key, false);
            return updateStatus;
        }

        public int DeleteAsync(int freightConfigurationId)
        {
            //1 通过freightConfigurationId获取车辆类型id
            var freightConfigInfo = GetByIdAsync(freightConfigurationId);
            //2 检查运费配置是否处于启用状态
            var vehicleTypeInfo = CheckVehicleType(freightConfigInfo.VehicleTypeId);
            if (vehicleTypeInfo.Enabled)
                throw new ArgumentException("车辆类型处于启用状态，不允许删除");

            var result = this.freightConfigurationData.DeleteAsync(freightConfigurationId).GetAwaiter().GetResult();
            string cacheKey = this.redisService.GetKey(key, freightConfigurationId);
            this.redisService.DeleteAsync(cacheKey);
            return result;
        }

        public FreightConfigurationDto GetByIdAsync(int freightConfigurationId)
        {
            var data = freightConfigurationData.GetByIdAsync(freightConfigurationId).Result;
            if (data == null)
                throw new ArgumentException("找不到相应的运费配置数据");

            return data.As<FreightConfigurationDto>();
        }

        public PageableList<FreightConfigurationDto> GetAllAsync(AdminDto admin, FreightConfigurationListRequestDto dto)
        {
            string cacheKey = this.redisService.GetKey(key, dto.As<FreightConfigurationViewEntity>());
            var result = this.redisService.TryGet(cacheKey, () =>
            {
                var (datas, dataCount) = freightConfigurationData.GetAsync(admin.AgentRecord?.AgentRecordId, dto.As<FreightConfigurationViewEntity>()).Result;
                return new PageableList<FreightConfigurationDto>()
                {
                    Count = dataCount,
                    Items = datas.As<List<FreightConfigurationDto>>()
                };
            }, TimeSpan.FromDays(30));
            return result;
        }

        #endregion 公共方法

        #region 私有方法

        /// <summary>
        /// 检查车辆类型是否存在
        /// </summary>
        /// <param name="vehicleTypeId">车辆类型id</param>
        /// <returns></returns>
        private VehicleTypeDto CheckVehicleType(int vehicleTypeId)
        {
            string cacheKey = this.redisService.GetKey(vehicleTypeKey, vehicleTypeId);
            var result = this.redisService.TryGet(cacheKey, () =>
            {
                var vehicleTypeInfo = vehicleTypeService.GetById(vehicleTypeId);
                if (vehicleTypeInfo == null)
                    throw new ArgumentException("车辆类型不存在");
                if (!vehicleTypeInfo.Enabled)
                    throw new ArgumentException("车型已被禁用，无法执行新增运费配置操作");
                return vehicleTypeInfo;
            }, TimeSpan.FromDays(1));
            return result;
        }

        /// <summary>
        /// 检查运费配置项是否已存在
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        private void CheckFreightConfig(FreightConfigurationRequestDto dto)
        {
            if (dto.VehicleCategoryReferenceIds.Count == 0)
            {
                throw new ArgumentException("车辆分类为必选项");
            }
            foreach (int id in dto.VehicleCategoryReferenceIds)
            {
                var vehicleTypeTask = vehicleTypeService.GetById(dto.VehicleTypeId);
                var vehicleCategoryReferenceTask = vehicleCategoryReferenceData.GetById(id).Result;
                if (vehicleCategoryReferenceTask == null)
                {
                    throw new ArgumentException("车辆类型分类信息不存在");
                }
                if (dto.VehicleTypeId != vehicleCategoryReferenceTask.VehicleTypeId)
                {
                    throw new ArgumentException("车型信息与车辆分类信息不匹配");
                }
                var freightConfigTask = freightConfigurationData.CheckFreightConfigurationExists(dto.VehicleTypeId, id, dto.ProvinceCode, dto.CityCode, dto.CountyCode, dto.SourceType, dto.FreightType).Result;
                if (freightConfigTask > 0)
                {
                    throw new ArgumentException(string.Format("车型：{0} {1} {2} {3} 运费配置已存在", vehicleTypeTask.VehicleTypeName, vehicleCategoryReferenceTask.CategoryValue, dto.SourceType.GetDescriptionString(), dto.FreightType.GetDescriptionString()));
                }
            }
        }

        /// <summary>
        /// 通过freightConfigId检查运费配置是否存在
        /// </summary>
        /// <param name="freightConfigId">运费配置id</param>
        /// <returns></returns>
        private void CheckFreightConfig(int freightConfigId)
        {
            var res = freightConfigurationData.CheckFreightConfigurationExists(freightConfigId).Result;
            if (res <= 0)
                throw new ArgumentException("运费配置不存在");
        }

        #endregion 私有方法


        /// <summary>
        /// 返回指定的运费配置
        /// </summary>
        /// <param name="vehicleTypeId">车型ID</param>
        /// <param name="vehicleCategoryReferenceId">车辆分类ID</param>
        /// <param name="ProvinceCode">省编码</param>
        /// <param name="CityCode">市编码</param>
        /// <param name="CountyCode">区县编码</param>
        /// <param name="sourceType">订单来源</param>
        /// <param name="freightType">物流类型</param>
        /// <returns></returns>
        public FreightConfigurationDto Get(int vehicleTypeId, int vehicleCategoryReferenceId, string ProvinceCode, string CityCode, string CountyCode, OrderSourceType? sourceType, FreightType? freightType)
        {
            string cacheKey = this.redisService.GetKey(key, vehicleTypeId, vehicleCategoryReferenceId, ProvinceCode, CityCode, CountyCode, sourceType, freightType);
            var result = this.redisService.TryGet(cacheKey, () =>
            {
                return freightConfigurationData.GetFreight(vehicleTypeId, vehicleCategoryReferenceId, ProvinceCode, CityCode, CountyCode, sourceType, freightType).GetAwaiter().GetResult().As<FreightConfigurationDto>();
            }, TimeSpan.FromDays(30));
            return result;
        }

        public bool InitRepair()
        {
            var result = this.freightConfigurationData.GetCountyCodeList().Result;
            var county = this.countyData.GetList().Result;
            if (result.Any() && county.Any())
            {
                if (result.Count() != county.Count())
                {
                    var tempList = county.Select(p => p.CountyCode).Except(result).ToList();
                    foreach (var item in tempList)
                    {
                        this.freightConfigurationData.InsertReparir(item, result.FirstOrDefault()).GetAwaiter().GetResult();
                    }
                    this.redisService.DeleteMultipleKey(key, false);
                }
            }
            var vehicleCategoryReferenceList = this.vehicleCategoryReferenceData.GetCategoryReferenceList(null).Result;
            var vehicleTypeIdAndReferenceIdList = this.freightConfigurationData.GetVehicleTypeIdAndReferenceIdList().Result;
            if (vehicleTypeIdAndReferenceIdList != null && vehicleTypeIdAndReferenceIdList.Any())
            {
                foreach (var item in vehicleCategoryReferenceList)
                {
                    if (!vehicleTypeIdAndReferenceIdList.Any(p => p.Key == item.VehicleCategoryReferenceId && p.Value == item.VehicleTypeId))
                    {
                        this.freightConfigurationData.InsertReparir(vehicleTypeIdAndReferenceIdList.FirstOrDefault(), item.VehicleCategoryReferenceId, item.VehicleTypeId).GetAwaiter().GetResult();
                    }
                }
                this.redisService.DeleteMultipleKey(key, false);
            }
            return true;
        }

        public IEnumerable<FreightConfigurationDto> GetList(AdminDto admin, string provinceCode, string cityCode, string countyCode, int? vehicleTypeId, OrderSourceType? sourceType, FreightType? freightType)
        {
            string cacheKey = this.redisService.GetKey(key, admin.AgentRecord?.AgentRecordId, provinceCode, cityCode, countyCode, vehicleTypeId, sourceType, freightType);
            var result = this.redisService.TryGet(cacheKey, () =>
            {
                var data = freightConfigurationData.GetList(admin.AgentRecord?.AgentRecordId, provinceCode, cityCode, countyCode, vehicleTypeId, sourceType, freightType).Result;
                return data.As<List<FreightConfigurationDto>>();
            }, TimeSpan.FromDays(30));
            return result;
        }
    }
}